package com.imooc.pan.server.modules.recycle.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.imooc.pan.core.constants.RPanConstants;
import com.imooc.pan.core.exception.RPanBusinessException;
import com.imooc.pan.server.common.event.file.FilePhysicalDeleteEvent;
import com.imooc.pan.server.common.event.file.FileRestoreEvent;
import com.imooc.pan.server.modules.file.converter.FileConverter;
import com.imooc.pan.server.modules.file.entity.RPanUserFile;
import com.imooc.pan.server.modules.file.enums.DelFlagEnum;
import com.imooc.pan.server.modules.file.service.RPanUserFileService;
import com.imooc.pan.server.modules.file.vo.RPanUserFileVo;
import com.imooc.pan.server.modules.recycle.context.DeleteContext;
import com.imooc.pan.server.modules.recycle.context.QueryRecycleFileContext;
import com.imooc.pan.server.modules.recycle.context.RestoreContext;
import com.imooc.pan.server.modules.recycle.service.RecycleService;
import org.assertj.core.util.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author hhb
 * @data 2024/6/15 22:27
 */
@Service
public class RecycleServiceImpl implements RecycleService, ApplicationContextAware {


    @Autowired
    private RPanUserFileService rPanUserFileService;

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }



    @Override
    public List<RPanUserFileVo> getRecycleFiles(QueryRecycleFileContext context) {
        Long userId = context.getUserId();
        LambdaQueryWrapper<RPanUserFile> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RPanUserFile::getUserId,userId);
        lqw.eq(RPanUserFile::getDelFlag, DelFlagEnum.YES.getCode());
        lqw.orderByDesc(RPanUserFile::getUpdateTime);
        List<RPanUserFile> list = rPanUserFileService.list(lqw);
        if(CollectionUtils.isEmpty(list)){
            return Lists.newArrayList();
        }
        List<RPanUserFileVo> result = list.stream().map(item -> {
            RPanUserFileVo vo = new RPanUserFileVo();
            BeanUtils.copyProperties(item, vo);
            return vo;
        }).collect(Collectors.toList());
        return result;
    }

    /**
     * 回收站文件还原
     * 检查操作权限
     * 检查是不是可以还原
     * 执行文件还原的操作
     * 执行文件还原的后置操作
     * @param context
     */
    @Override
    public void restore(RestoreContext context) {
        checkRestorePermission(context);
        checkRestoreFile(context);
        doRestore(context);
        afterRestore(context);
    }

    /**
     * 彻底删除文件
     * 检查操作权限
     * 递归查找子文件
     * 执行文件删除的动作
     * 删除的后置动作
     * @param context
     */
    @Override
    public void delete(DeleteContext context) {
        checkDeletePermission(context);
        findAllRecords(context);
        doDelete(context);
        afterDelete(context);
    }

    /**
     * 删除的后置动作
     * @param context
     */
    private void afterDelete(DeleteContext context){
        FilePhysicalDeleteEvent event = new FilePhysicalDeleteEvent(this,context.getAllRecords());
        applicationContext.publishEvent(event);
    }

    /**
     * 执行文件删除的动作
     * @param context
     */
    private void doDelete(DeleteContext context) {
        List<RPanUserFile> allRecords = context.getAllRecords();
        Set<Long> fileIdList = allRecords.stream().map(RPanUserFile::getFileId).collect(Collectors.toSet());
        rPanUserFileService.removeByIds(fileIdList);
    }

    /**
     * 递归查找子文件
     * @param context
     */
    private void findAllRecords(DeleteContext context) {
        List<RPanUserFile> records = context.getRecords();
        List<RPanUserFile> allRecords = rPanUserFileService.findAllRecords(records);
        context.setAllRecords(allRecords);
    }

    /**
     * 检查操作权限
     * @param context
     */
    private void checkDeletePermission(DeleteContext context) {
        List<Long> fileIdList = context.getFileIdList();
        List<RPanUserFile> records = rPanUserFileService.lambdaQuery().in(RPanUserFile::getFileId, fileIdList).list();
        if(CollectionUtils.isEmpty(records)){
            throw new RPanBusinessException("文件信息不存在");
        }
        Set<Long> userIdSet = records.stream().map(RPanUserFile::getUserId).collect(Collectors.toSet());
        if(userIdSet.size() > 1){
            throw new RPanBusinessException("您无权执行删除操作");
        }
        if(!userIdSet.contains(context.getUserId())){
            throw new RPanBusinessException("您无权执行删除操作");
        }
        context.setRecords(records);
    }

    /**
     * 执行文件还原的后置操作，发布事件
     * @param context
     */
    private void afterRestore(RestoreContext context) {
        FileRestoreEvent event = new FileRestoreEvent(this,context.getFileIdList());
        applicationContext.publishEvent(event);
    }

    private void doRestore(RestoreContext context) {
        List<RPanUserFile> records = context.getRecords();
        records.stream().forEach(record->{
            record.setDelFlag(DelFlagEnum.NO.getCode());
            record.setUpdateTime(new Date());
            record.setUpdateUser(context.getUserId());
        });
        if(!rPanUserFileService.updateBatchById(records)){
            throw new RPanBusinessException("还原文件失败");
        }
    }

    /**
     * 检查要还原的文件名称是不是被占用
     * 要还原的文件列表中有同一个文件夹下面相同名称的文件，不允许还原
     * 要还原的文件当前的父文件夹下面存在同名文件，不允许还原
     * @param context
     */
    private void checkRestoreFile(RestoreContext context) {
        List<RPanUserFile> records = context.getRecords();
        //进行名称拼接查找是否有同名文件
        Set<String> filenameSet = records.stream().map(record -> record.getFilename() + RPanConstants.COMMON_SEPARATOR + record.getParentId()).collect(Collectors.toSet());
        if(filenameSet.size() != records.size()){
            throw new RPanBusinessException("要还原的文件列表中存在同一个文件夹下有同名文件的情况，请逐个还原");
        }
        for (RPanUserFile record : records) {
            Integer count = rPanUserFileService.lambdaQuery()
                    .eq(RPanUserFile::getUserId, context.getUserId())
                    .eq(RPanUserFile::getFilename, record.getFilename())
                    .eq(RPanUserFile::getDelFlag, DelFlagEnum.NO.getCode())
                    .eq(RPanUserFile::getParentId, record.getParentId())
                    .count();
            //存在同名文件
            if(count > 0){
                throw new RPanBusinessException("文件" + record.getFilename() + "还原失败，其父文件夹下已存在同名文件或同名文件夹，请重命名后再执行还原操作");
            }
        }
    }

    /**
     * 检查操作权限
     * @param context
     */
    private void checkRestorePermission(RestoreContext context) {
        List<Long> fileIdList = context.getFileIdList();
        List<RPanUserFile> records = rPanUserFileService.lambdaQuery().in(RPanUserFile::getFileId, fileIdList).list();
        if(CollectionUtils.isEmpty(records)){
            throw new RPanBusinessException("文件信息不存在");
        }
        Set<Long> userIdSet = records.stream().map(RPanUserFile::getUserId).collect(Collectors.toSet());
        if(userIdSet.size() > 1){
            throw new RPanBusinessException("您无权执行还原文件操作");
        }
        if(!userIdSet.contains(context.getUserId())){
            throw new RPanBusinessException("您无权执行还原文件操作");
        }
        context.setRecords(records);
    }

}
